<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework\annotations\parsers;

#use tehframework\testing\phpunit\TestCase;
#use tehframework\annotations\FakeAnnotation;

require_once 'test/testHelper.php';

class AnnotationParserImplTest extends TestCase
{
	/**
	 * @var AnnotationParserImpl
	 */
	protected $parser;
	
	/**
	 * @var ArrayParser
	 */
	protected $arrayParser;
	
	protected function beforeTest()
	{
		$this->arrayParser = $this->mock('ArrayParser');
		$this->parser      = new AnnotationParserImpl($this->arrayParser);
	}
	
	function testParseAnnotationThrowsExceptionIfValueIsNotValidStringRepresentationOfAnnotation()
	{
		$this->setExpectedException('ParserException');
		
		$this->parser->parseAnnotation('Fake');
	}
	
	function testParseAnnotationParsesAnnotationWithNameOnly()
	{
		$annotation = '@Fake';
		
		$this->expect->never($this->arrayParser);
		
		$this->assertAnnotationDefinition(
			$expected = array('name' => 'Fake'),
			$this->parser->parseAnnotation($annotation));
		
		$this->assertAnnotationDefinition($expected,
			$this->parser->parseAnnotation($annotation . '()'));
	}
	
	function testParseAnnotationParsesAnnotationWithType()
	{
		$annotation = '@Fake<MoreFake>';
		
		$this->expect->never($this->arrayParser);
		
		$this->assertAnnotationDefinition(
			$expected = array('name' => 'Fake', 'type' => 'MoreFake'),
			$this->parser->parseAnnotation($annotation));
		
		$this->assertAnnotationDefinition($expected,
			$this->parser->parseAnnotation($annotation . '()'));
	}
	
	function testParseAnnotationParsesAnnotationWithParameter()
	{
		$annotation = '@Fake[fakeParam]';
		
		$this->expect->never($this->arrayParser);
		
		$this->assertAnnotationDefinition(
			$expected = array('name' => 'Fake', 'parameter' => 'fakeParam'),
			$this->parser->parseAnnotation($annotation));
		
		$this->assertAnnotationDefinition($expected,
			$this->parser->parseAnnotation($annotation . '()'));
	}
	
	function testParseAnnotationParsesAnnotationWithTypeAndParameter()
	{
		$annotation = '@Fake<MoreFake>[fakeParam]';
		
		$this->expect->never($this->arrayParser);
		
		$expected = array(
			'name' => 'Fake', 'type' => 'MoreFake', 'parameter' => 'fakeParam');
		
		$this->assertAnnotationDefinition($expected,
			$this->parser->parseAnnotation($annotation));
		
		$this->assertAnnotationDefinition($expected,
			$this->parser->parseAnnotation($annotation . '()'));
	}
	
	function testParseAnnotationParsesAnnotationWithArgumentsOnly()
	{
		$annotation = '@Fake(1, 2, "three", [4, 5], 6)';
		$arguments  = array(1, 2, 'three', array(4, 5), 6);
		
		$this
			->expect
			->one($this->arrayParser)
			->parseArray('{1, 2, "three", [4, 5], 6}')
			->returns($arguments);
		
		$this->assertAnnotationDefinition(
			array('name' => 'Fake', 'arguments' => $arguments),
			$this->parser->parseAnnotation($annotation));
	}
	
	function testParseAnnotationParsesAnnotationWithPropertiesOnly()
	{
		$annotation = '@Fake(one=1, two=2, three="three", four=[4, 5], ' .
			'five={six: 6})';
		$properties = array(
			'one'   => 1,
			'two'   => 2,
			'three' => 'three',
			'four'  => array(4, 5),
			'five'  => array('six' => 6));
		
		$this
			->expect
			->one($this->arrayParser)
			->parseArray('{one=1, two=2, three="three", four=[4, 5], five={six: 6}}')
			->returns($properties);
		
		$this->assertAnnotationDefinition(
			array('name' => 'Fake', 'properties' => $properties),
			$this->parser->parseAnnotation($annotation)
		);
	}
	
	function testParseAnnotationParsesAnnotationWithArgumentsAndProperties()
	{
		$annotation = '@Fake(1, two=2, 3, four=4, 5, [])';
		$arguments  = array(1, 3, 5, array());
		$properties = array('two' => 2, 'four' => 4);
		
		$this
			->expect
			->one($this->arrayParser)
			->parseArray('{1, two=2, 3, four=4, 5, []}')
			->returns($arguments + $properties);
		
		$this->assertAnnotationDefinition(array(
				'name'       => 'Fake',
				'arguments'  => $arguments,
				'properties' => $properties),
			$this->parser->parseAnnotation($annotation)
		);
	}
	
	function testParseAnnotationParsesAnnotationWithAllDetails()
	{
		$annotation = '@Fake<MoreFake>[fakeParam]("fake arg", fake="prop")';
		$arguments  = array('fake arg');
		$properties = array('fake' => 'prop');
		
		$this
			->expect
			->one($this->arrayParser)
			->parseArray('{"fake arg", fake="prop"}')
			->returns($arguments + $properties);
		
		$this->assertAnnotationDefinition(
			array(
				'name'       => 'Fake',
				'type'       => 'MoreFake',
				'parameter'  => 'fakeParam',
				'arguments'  => $arguments,
				'properties' => $properties
			),
			$this->parser->parseAnnotation($annotation));
	}
	
	protected function assertAnnotationDefinition(
		array $expected, AnnotationDefinition $def)
	{
		$expected += array(
			'type'       => null,
			'parameter'  => null,
			'arguments'  => array(),
			'properties' => array());
		
		$this->assertSame($expected['name'], $def->name, 'Invalid name.');
		$this->assertSame($expected['type'], $def->type, 'Invalid type.');
		$this->assertSame($expected['parameter'], $def->parameter,
			'Invalid parameter.');
		$this->assertSame($expected['arguments'], $def->arguments,
			'Invalid arguments.');
		$this->assertSame($expected['properties'], $def->properties,
			'Invalid properties.');
	}
}